package com.xwj.nio.netty.decoder;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.ReplayingDecoder;

import java.nio.charset.Charset;
import java.util.List;

/**
 * @author xwj
 * @date 2020/6/23
 */
public class StringReplayDecoderTest {
    public static void main(String[] args) {
        // 创建嵌入式通道
        EmbeddedChannel channel = new EmbeddedChannel(new ChannelInitializer<EmbeddedChannel>() {
            @Override
            protected void initChannel(EmbeddedChannel ch) {
//                ch.pipeline().addLast(new StringReplayDecoder());
                ch.pipeline().addLast(new StringDecoder());
                ch.pipeline().addLast(new StringProcessHandler());
            }
        });
        byte[] bytes = "海内存知己天涯若比邻".getBytes(Charset.forName("utf-8"));
        for (int j = 1; j <= 10; j++) {
            ByteBuf buf = Unpooled.buffer();
            // 写入字符串长度
            buf.writeInt(bytes.length * j);
            // 写入字符串
            for (int i = 0; i < j; i++) {
                buf.writeBytes(bytes);
            }
            channel.writeInbound(buf);
        }
    }
}

class StringReplayDecoder extends ReplayingDecoder<StringReplayDecoder.Status> {
    enum Status {
        PARSE_1, PARSE_2
    }

    private int length;
    private byte[] inBytes;

    public StringReplayDecoder() {
        super(Status.PARSE_1);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        switch (state()) {
            case PARSE_1:
                //第一步，从装饰器ByteBuf 中读取长度
                length = in.readInt();
                inBytes = new byte[length];
                // 进入第二步，读取内容
                // 并且设置“读指针断点”为当前的读取位置
                checkpoint(Status.PARSE_2);
                break;
            case PARSE_2:
                //第二步，从装饰器ByteBuf 中读取内容数组
                in.readBytes(inBytes, 0, length);
                out.add(new String(inBytes, "UTF-8"));
                // 第二步解析成功，
                // 进入第一步，读取下一个字符串的长度
                // 并且设置“读指针断点”为当前的读取位置
                checkpoint(Status.PARSE_1);
                break;
            default:
                break;
        }
    }
}

class StringDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
        // 读取消息头
        if(buf.readableBytes() < 4) {
            return;
        }
        // 头已经完整
        // 在真正开始从buffer读取数据之前，调用markReaderIndex()设置回滚点
        // 回滚点为header的readIndex位置
        buf.markReaderIndex();
        int length = buf.readInt();
        // 从缓冲区读出消息头的大小，会使得readIndex读指针前移
        // 剩余长度不够消息体，重置读指针
        if(buf.readableBytes() < length) {
            // 读指针回滚到消息头的readIndex处
            buf.resetReaderIndex();
            return;
        }
        // 读取数据
        byte[] inBytes = new byte[length];
        buf.readBytes(inBytes, 0, length);
        out.add(new String(inBytes, "UTF-8"));
    }
}

class StringProcessHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String s = (String) msg;
        System.out.println("打印出一个字符串: " + s);
    }
}
